﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using SharpDX;
using SharpDX.Direct3D;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D11;
using SharpDX.DXGI;

namespace SharpDX
{
    public class CTextureShader
    {
        public struct Vertex
        {
            public Vector3 position;
            public Vector2 texture;
        }
        public struct MatrixBuffer
        {
            public Matrix world;
            public Matrix view;
            public Matrix projection;
        }
        public VertexShader vertexShader;
        public PixelShader pixelShader;
        public InputLayout inputLayout;
        public SharpDX.Direct3D11.Buffer constMatrixBuffer;
        public SamplerState samplerState;

        public CTextureShader() { }
        public bool Initialize(SharpDX.Direct3D11.Device device,IntPtr hwnd)
        {

            return InitializeShader(device, hwnd, "Texture.vs", "Texture.ps");
        }
        public bool InitializeShader(SharpDX.Direct3D11.Device device,IntPtr hwnd,string vsFileName,string psFileName)
        {

            vsFileName = @"..\..\Shaders\" + vsFileName;
            psFileName = @"..\..\Shaders\" + psFileName;

            var vertexShaderByteCode = ShaderBytecode.CompileFromFile(vsFileName, "TextureVertexShader", "vs_4_0", ShaderFlags.None, EffectFlags.None);
            var pixelShaderByteCode = ShaderBytecode.CompileFromFile(psFileName, "TexturePixelShader", "ps_4_0", ShaderFlags.None, EffectFlags.None);

            vertexShader = new VertexShader(device, vertexShaderByteCode);
            pixelShader = new PixelShader(device, pixelShaderByteCode);

            var inputElements = new InputElement[]
            {
                new InputElement()
					{
						SemanticName = "POSITION",
						SemanticIndex = 0,
						Format = Format.R32G32B32_Float,
						Slot = 0,
						AlignedByteOffset = 0,
						Classification = InputClassification.PerVertexData,
						InstanceDataStepRate = 0
					},
					new InputElement()
					{
						SemanticName = "TEXCOORD",
						SemanticIndex = 0,
						Format = Format.R32G32B32_Float,
						Slot = 0,
						AlignedByteOffset = 12,//Vertex.AppendAlignedElement,
						Classification = InputClassification.PerVertexData,
						InstanceDataStepRate = 0
					}
            };

            inputLayout = new InputLayout(device, ShaderSignature.GetInputSignature(vertexShaderByteCode), inputElements);

            vertexShaderByteCode.Dispose();
            pixelShaderByteCode.Dispose();

            var matrixBufferDesc=new BufferDescription()
            {
                BindFlags= BindFlags.ConstantBuffer,
                CpuAccessFlags= CpuAccessFlags.Write,
                OptionFlags= ResourceOptionFlags.None,
                SizeInBytes=Utilities.SizeOf<MatrixBuffer>(),
                StructureByteStride=0,
                Usage= ResourceUsage.Dynamic,
            };

            constMatrixBuffer = new Direct3D11.Buffer(device, matrixBufferDesc);
            var samplerDesc = new SamplerStateDescription()
            {
                Filter = Filter.MinMagMipLinear,
                AddressU = TextureAddressMode.Wrap,
                AddressV = TextureAddressMode.Wrap,
                AddressW = TextureAddressMode.Wrap,
                MipLodBias = 0,
                MaximumAnisotropy = 1,
                ComparisonFunction = Comparison.Always,
                BorderColor = new Color4(0, 0, 0, 0),
                MinimumLod = 0,
                MaximumLod = 0
            };
            samplerState = new SamplerState(device, samplerDesc);

            return true;
        }
        public void ShutDown()
        {
            if (samplerState != null)
            {
                samplerState.Dispose();
                samplerState = null;
            }

            // Release the matrix constant buffer.
            if (constMatrixBuffer != null)
            {
                constMatrixBuffer.Dispose();
                constMatrixBuffer = null;
            }

            // Release the layout.
            if (inputLayout != null)
            {
                inputLayout.Dispose();
                inputLayout = null;
            }

            // Release the pixel shader.
            if (pixelShader != null)
            {
                pixelShader.Dispose();
                pixelShader = null;
            }

            // Release the vertex shader.
            if (vertexShader != null)
            {
                vertexShader.Dispose();
                vertexShader = null;
            }
        }

        public void SetShaderParameters(DeviceContext devicecontext,Matrix worldmat,Matrix viewmat,Matrix projectionmat,ShaderResourceView textureRes)
        {
            worldmat.Transpose();
            viewmat.Transpose();
            projectionmat.Transpose();

            DataStream mappedRes;

            devicecontext.MapSubresource(constMatrixBuffer, MapMode.WriteDiscard, SharpDX.Direct3D11.MapFlags.None, out mappedRes);
            var matrixBuffer = new MatrixBuffer()
            {
                world = worldmat,
                view = viewmat,
                projection = projectionmat
            };
            mappedRes.Write(matrixBuffer);

            devicecontext.UnmapSubresource(constMatrixBuffer, 0);

            var bufferNum = 0;
            devicecontext.VertexShader.SetConstantBuffer(bufferNum, constMatrixBuffer);
            devicecontext.PixelShader.SetShaderResource(0, textureRes);

        }
        public void RenderShader(DeviceContext devicecontext,int indexCount)
        {
            devicecontext.InputAssembler.InputLayout = inputLayout;

            devicecontext.VertexShader.Set(vertexShader);
            devicecontext.PixelShader.Set(pixelShader);

            devicecontext.PixelShader.SetSampler(0, samplerState);
            devicecontext.DrawIndexed(indexCount, 0, 0);
        }
        public void Render(DeviceContext devicecontext,int indexCount,Matrix world,Matrix view,Matrix projection,ShaderResourceView texture)
        {
            SetShaderParameters(devicecontext, world, view, projection, texture);

            RenderShader(devicecontext, indexCount);
        }
        
    }
}
